#!/usr/bin/env python
# -*- coding: utf-8 -*-
''' Programme Puissance 4 (IA !)
'''

import random


nb_col = 7
nb_li = 6
vide = 0


def detection_align(n, ligne, couleur):
    ''' Détecte une séquence de 4 contenant
        n pions de la couleur
        (n=1 ou 2 car 3 est à part...)
        et le reste vide.
        On renvoie les indices des cases vides.
    '''
    a_jouer = []
    for k in range(len(ligne)-n):
        if ligne[k:k+4].count(couleur)==n and ligne[k:k+4].count(vide)==4-n:
            for p,e in enumerate(ligne[k:k+4]):
                if e==vide:
                    a_jouer.append(p+k)
    return a_jouer


def diag_li(planche, i, j, sens):
    '''Retourne une liste des éléments de la diagonale
    qui part de (i,j)
    vers le bas à droite si sens vaut 1
    et vers le bas à gauche si sens vaut -1
    '''
    ligne = []
    ii, jj = i, j
    while ii<nb_li and 0<=jj<nb_col:
        ligne.append(planche[ii][jj])
        ii += 1
        jj = jj + sens
    return ligne


def col_li(planche, j):
    ''' Transforme une colonne en ligne...'''
    return [planche[i][j] for i in range(nb_li)]


def detection_coup_final(planche, couleur):
    """Différente des autres détection car il suffit de
    prendre la première qui vient en attaque puis
    sinon la première qui vient en défense,
    alors que pour les autres sont listées pour
    choisir les optimales...
    Le coup est donné dans un tuple d'un élément
    ou False sinon, ainsi on peut tester coup comme un booléen :
    0 compterait comme faux mais (0,) compte comme vrai.
    """
    # Lignes de 3 :
    for i, ligne in enumerate(planche):
        detect_align = detection_align(3, ligne, couleur)
        for j in detect_align:
            if i==nb_li-1 or planche[i+1][j]!=vide:
##                print('ligne 3, i, j couleur : ', i, j, couleur)
                return (j,)
    # Colonnes de 3 :
    for j in range(nb_col):
        detect_align = detection_align(3, col_li(planche, j), couleur)
        for i in detect_align:
            if i==nb_li-1 or planche[i+1][j]!=vide:
##                print('colonne 3')
                return (j,)
    # Diagonales de 3 :
    for j in range(nb_col):
        for sens in (-1,1):        
            detect_align = detection_align(3, diag_li(planche, 0, j, sens), couleur)   
            for i in detect_align:
                k = j + sens*i
                if i==nb_li-1 or planche[i+1][k]!=vide:
##                    print('diag 3j', sens)
                    return (k,)
    for i in range(nb_li):       
        detect_align = detection_align(3, diag_li(planche, i, 0, 1), couleur)   
        for j in detect_align:
            k = i + j
            if k==nb_li-1 or planche[k+1][j]!=vide:
##                print('diag 3i', 1)
                return (j,)
        detect_align = detection_align(3, diag_li(planche, i, nb_col-1, -1), couleur)
##        print('detect_align : ', detect_align)
        for j in detect_align:
            k = i + j
##            print('k, j, i : ', k, j, i) 
            if k==nb_li-1 or planche[k+1][nb_col-1-j]!=vide:
##                print('diag 3i', -1)
                return (nb_col-1-j,)
    return False


def detection(planche, couleur, nb, liste_a_jouer):
    ''' Détecte une position libre et possible qui amène à
    nb+1 l'effectif d'une séquence de nb parmi 4
    (pas nécessairement consécutifs).
    Ajoute les coups possibles dans liste_a_jouer (effet de bord).
    '''
    
    # Lignes de nb :
    for i, ligne in enumerate(planche):
        detect_align = detection_align(nb, ligne, couleur)
        for j in detect_align:
            if i==nb_li-1 or planche[i+1][j]!=vide:
                liste_a_jouer.append(j)
    # Colonnes de nb :
    for j in range(nb_col):
        detect_align = detection_align(nb, col_li(planche, j), couleur)
        for i in detect_align:
            if i==nb_li-1 or planche[i+1][j]!=vide:
                liste_a_jouer.append(j)
    # Diagonales de nb :
    for j in range(nb_col):
        for sens in (-1,1):        
            detect_align = detection_align(nb, diag_li(planche, 0, j, sens), couleur)
            for i in detect_align:
                k = j + sens*i
                if i==nb_li-1 or planche[i+1][k]!=vide:
                    liste_a_jouer.append(k)
    for i in range(nb_li):       
        detect_align = detection_align(nb, diag_li(planche, i, 0, 1), couleur)  
        for j in detect_align:
            k = i + j
            if k==nb_li-1 or planche[k+1][j]!=vide:
                liste_a_jouer.append(j)    
        detect_align = detection_align(nb, diag_li(planche, i, nb_col-1, -1), couleur)
        for j in detect_align:
            k = i + j
##            print('nb, k, j, i : ', nb, k, j, i) 
            if k==nb_li-1 or planche[k+1][nb_col-1-j]!=vide:
                liste_a_jouer.append(nb_col-1-j)


def duplique_planche(planche):
    copie_planche = []
    for ligne in planche:
        copie_planche.append([])
        for e in ligne:
            copie_planche[-1].append(e)
##    print('copie_planche : ', copie_planche)
    return copie_planche
            

def choix_coup_a_jouer(liste_a_jouer):
    """ Tire au sort une des colonnes de la liste
    dont la fréquence est maximale.
    """   
    
    dico = {}
    for e in liste_a_jouer:
        dico[e] = dico.get(e,0) + 1
    maxi = 0
    liste_maxi = []
    for k, v in dico.items():
        if v>maxi:
            maxi = v
            liste_maxi = [k]
        elif v==maxi:
            liste_maxi.append(k)
##    print('liste_a_jouer, dico, maxi : ', liste_a_jouer, dico, maxi)
    return random.choice(liste_maxi)

    
def joueur_g(planche, couleur):

    couleur_adverse = 3 - couleur
    # Attaque ultime (3->4 !)
    coup = detection_coup_final(planche, couleur)
    if coup:
        return coup[0]

    # Défense ultime (3->4 !)
    coup = detection_coup_final(planche, couleur_adverse)
    if coup:
##        print('défense')
        return coup[0]
        
    liste_a_jouer =[]
    
    # Attaque pour 2->3
    detection(planche, couleur, 2, liste_a_jouer)
    
    # Défense pour 2->3
    detection(planche, couleur_adverse, 2, liste_a_jouer)
    # On retire de la liste les coups
    # qui mèneraient à un coup de grâce par l'adversaire
    copie_liste_a_jouer = liste_a_jouer[:]
    for j in copie_liste_a_jouer:
        copie_planche = duplique_planche(planche)
        depot_jeton(copie_planche, j, couleur)
        coup = detection_coup_final(copie_planche, couleur_adverse)
        if coup:
            liste_a_jouer.remove(j)

    if liste_a_jouer:
        return choix_coup_a_jouer(liste_a_jouer)

    # Attaque pour 1->2
    detection(planche, couleur, 1, liste_a_jouer)
    
    # Défense pour 1->2
    detection(planche, couleur_adverse, 1, liste_a_jouer)
    
    # On retire encore de la liste les coups
    # qui mèneraient à un coup de grâce par l'adversaire
    # sauf le dernier car il faut bien jouer !
    # (on pourrait aussi encore essayer autre chose mais c'est compliqué)
    copie_liste_a_jouer = liste_a_jouer[:]
    for j in copie_liste_a_jouer:
        copie_planche = duplique_planche(planche)
        depot_jeton(copie_planche, j, couleur)
        coup = detection_coup_final(copie_planche, couleur_adverse)
        if coup:
            if len(liste_a_jouer)>1:
                liste_a_jouer.remove(j)
            else:
                break
    if liste_a_jouer:        
        return choix_coup_a_jouer(liste_a_jouer)
    else: # Ici on pourrait essayer d'éviter les coups dangereux...
        return random.choice([i for i,e in enumerate(planche[0]) if e==vide])


def depot_jeton(planche, j, couleur):
    ''' utilisée uniquement sur une copie de la planche !
    '''
##    print('planche : ', planche)
    if not(planche[0][j]):
        i = nb_li-1
##        print('i,j : ',i,j)
        while planche[i][j]:
##            print('while, i, couleur : ', i, couleur)
            i = i-1
##        print('i :', i)
        planche[i][j] = couleur
        deposer = True
    else:
        deposer = False
    return deposer
